#include "Task.hpp"
#include <unistd.h>
#include <assert.h>
#include <cstring>
#include <string>
#include <iostream>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>

// 子进程数
const int processnum = 10;

// 任务列表
std::vector<task_t> tasks;

// 先描述
struct channel
{
    channel(int cmdfd, pid_t slaverid, std::string processname)
        :_cmdfd(cmdfd), _slaverid(slaverid), _processname(processname)
    {}

    int _cmdfd;               // 发送文件的文件描述符
    pid_t _slaverid;          // 执行进程的id
    std::string _processname; // 执行进程的名字——便于打印日志
};

void Slaver()
{
    while(true)
    {
        int cmdcode = 0;
        // 从 0(stdin) 处读取数据，需要使用重定向dup2，此时0是pipefd[0]的拷贝
        int n = read(0, &cmdcode, sizeof(int)); // 若父进程没有子进程发送数据，则处于阻塞等待状态
        if(n == sizeof(int))
        {
            //执行cmdcode对应的任务列表
            std::cout <<"slaver say@ get a command: "<< getpid() << " : cmdcode: " <<  cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if(n == 0) break;
    }
}

// 初始化
void InitProcessPool(std::vector<channel>* channels)
{
    // 使用oldfd保证每个子进程均只能有一个写端
    std::vector<int> oldfds;

    for (int i = 0; i < processnum; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd);
        assert(!n);
        (void)n;

        pid_t id = fork();
        // child
        if (id == 0)
        {
            for (auto& fd : oldfds) close(fd);
            close(pipefd[1]);
            // 改变重定向，便 0(stdin) 成为pipefd[0]的拷贝
            dup2(pipefd[0], 0);
            close(pipefd[0]);
            Slaver();

            // 子进程退出提示
            std::cout << "process " << getpid() << ": quit" << std::endl;
            exit(0); 
        }

        // father
        close(pipefd[0]);

        // 将当前进程添加进channels中
        std::string processname = "process " + std::to_string(i);
        (*channels).push_back(channel(pipefd[1], getpid(), processname));
        oldfds.push_back(pipefd[1]);
    }
}

// 菜单目录
void Menu()
{
    std::cout << "#################################################" << std::endl;
    std::cout << "##############     1. 更新日志     ###############" << std::endl;
    std::cout << "###########      2. 装填武器子弹      #############" << std::endl;
    std::cout << "###   3. 检测软件是否更新,如果需要，就提示用户    ###" << std::endl;
    std::cout << "#####       4. 用户打出子弹,更新子弹数量       #####" << std::endl;
    std::cout << "##############       0. 退出       ###############" << std::endl;
    std::cout << "##################################################" << std::endl;
}

// 交由子进程执行任务
void ctrlSlaver(const std::vector<channel>& channels)
{
    int which = 0;
    while (true)
    {
        // 1. 选择任务
        // int cmdcode = rand()%30;
        int select = 0;
        Menu();
        std::cout << "请输入要执行的任务 :";
        std::cin >> select;

        if (select == 0) break;
        if (select < 0 || select >= 5) 
        {
            std::cout << "输入有误，请重新输入!" << std::endl;
            continue;
        }
        int cmdcode = select - 1;

        // 2. 选择进程
        // int processpos = rand()%channels.size();

        // 3. 发送任务
        write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode));
        which++;
        which %= channels.size();
    }
}

// 退出
void ProcessQuit(const std::vector<channel>& channels)
{
    for (const auto& c : channels)
    {
        close(c._cmdfd);
        waitpid(c._slaverid, nullptr, 0);
    }
}

int main()
{
    // 再组织
    std::vector<channel> channels;

    // 种下随机数种子，便于后续使用随机数
    srand(time(nullptr) ^ getpid() ^ 2534);

    // 1. 初始化
    InitProcessPool(&channels); 

    std::cout << "Process Pool Init success!" << std::endl; 

    // 2. 开始控制子进程
    ctrlSlaver(channels);

    // 3. 退出
    ProcessQuit(channels);

    return 0;
}